MVC ASP.NET Cascading DropDownLists

31May09

Creating DropDownLists using WebForms is easy enough, for those who are venturing into the the world of MVC this may not be as straight forward.

I’ll be using the classic scenario of Cars and their related makes and models.

CarController.cs

public class CarFormViewModel
{
    MakeModelRepository makeModelRepository = 
    new MakeModelRepository();

    private string _carMake = "";

    public Car Car { get; private set; }
    public SelectList CarMakes { get; private set; }
    public SelectList CarModels { get; private set; }
    public string CarMake
    {
        get { return _carMake; }
        set { _carMake = value; }
    }

    // Constructor
    public CarFormViewModel(Car car)
    {
        Car = car;
        CarMakes = new SelectList(makeModelRepository.
        GetCarMakes(), "Value", "Text");

        if (car.CarMakeModelId > 0)
            CarMake = car.CarMakeModel.CarMake;

        CarModels = new SelectList(makeModelRepository.
        GetCarModels(CarMake), "Value", "Text", Car.CarMakeModelId);
    }
}

Something to be aware of is the use of valueSelected when defining your SelectList, make sure your view items e.g. DropDownList names match your database field names. I found the selected value was not being set correctly when editing a records.

CarForm.aspx

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl
<Vehicles.Controllers.CarFormViewModel>" %>

<script type="text/javascript">
    $(function() {
        $("#CarMake").change(function() {
        var carMake = $("#CarMake > option:selected").attr("value");
        var urlAction = "<%= Url.Action("FindCarModels", "Car") %>";
        $.getJSON(urlAction, { carMake: carMake }, function(data) {
                $("#CarMakeModelId").addItems(data);
            });
        });
    });
</script>

<%= Html.ValidationSummary("Edit was unsuccessful. Please correct 
the errors and try again.") %>
<% using (Html.BeginForm())
   {%>
<fieldset>
    <legend>Car</legend>
    <p>
        <label for="CarMake">
            Make:</label>
        <%= Html.DropDownList("CarMake", Model.CarMakes) %>
        <%= Html.ValidationMessage("CarMake", "*") %>
    </p>
    <p>
        <label for="CarMakeModelId">
            Model:</label>
        <%= Html.DropDownList("CarMakeModelId", Model.CarModels) %>
        <%= Html.ValidationMessage("CarMakeModelId", "*") %>
    </p>
    <p>
        <input type="submit" value="Save" />
    </p>
</fieldset>
<% } %>
<div>
    <%=Html.ActionLink("Back to List", "Index") %>
</div>

Make sure to change the inherits property to reference your new ViewModel. Note the use of the Url.Action helper, this little snippet helps you creates the correct path to your controller action without the use of those dots e.g. …/<controller>/<action>/

jHelper.js

Borrowed from Steve Michelotti.

$.fn.clearSelect = function() {
    return this.each(function() {
        if (this.tagName == 'SELECT')
            this.options.length = 0;
    });
}

$.fn.addItems = function(data) {
    return this.clearSelect().each(function() {
        if (this.tagName == 'SELECT') {
            var dropdownList = this;
            $.each(data, function(index, optionData) {
            var option = new Option(optionData.Text,
                         optionData.Value);

            if ($.browser.msie) {
                dropdownList.add(option);
            }
            else {
                dropdownList.add(option, null);
            }
            });
        }
    });
}

Little helper that clears and adds items to DropDownLists.

Something I found frustrating is when in debug mode a script reference would work find but when running using IIS the script would not load correctly. What you had to do was use those good old dots to get it loading correctly. My tip is to use the ResovleUrl helper.

<script src="<%= ResolveUrl("~/Scripts/jquery-1.3.1.js") %>" 
type="text/javascript"></script>

<script src="<%= ResolveUrl("~/Scripts/jHelper.js") %>" 
type="text/javascript"></script>

There are some workarounds I’ve seen that don’t break intellisense but look a bit messy. I figure I’ve lived without it for this long.

Apologies for not being able to attach the source code, if there is anything that is unclear or you have any questions feel free to email me.



7 Responses to “MVC ASP.NET Cascading DropDownLists”

  1. 1 gkochanowsky

    Can you provide a buildable project for that example?

  2. 3 gkochanowsky

    Thanks for posting the source.

  3. 4 Raman

    Hi Dien,

    Awesome post.

    I’ve been doing my first MVC project and got stuck on MVC cascading dropdownlists. After going through Michael Baird’s post, I could get the initial display working. However, I couldn’t get the post-back to work properly. I ran into two problems:

    1. The values I selected in the cascaded dropdownlist didn’t make it back to teh controller (model value in your example).

    2. On post-back, I couldn’t get the first dropdownlist’s selected value to be pre-selected (make value in your example).

    Going through your example, I don’t see any mistakes I made. I’ve to look through again with a fine-toothed comb.

    However, I like the simplicity of your post. Very well done.

    I learnt a couple of new things as well:

    1. Resolve URL
    2. Using helper functions (AddItems)
    3. No RouteMaps needed

    Thanks again for sharing your knowledge.
    Raman

    • Thanks for your great comments, glad I could help.

      Since the post I’ve have been using Url.Content instead of Resolve URL, the the former being available via intellisense.

      Without seeing your code I can’t know the problem, if you haven’t done already download the project at http://kmsystems.squarespace.com/journal/

  4. 6 tom

    great code thanks!

  5. This article describes the postback in ASP.NET MVC using dropdownlist example:

    http://www.altafkhatri.com/Altaf/ASP_NET_MVC_Postback/Dropdownlist_Example/PostBack


Leave a comment